//==--- tools/clang-check/ClangInterpreter.cpp - Clang Interpreter tool --------------===//
//===----------------------------------------------------------------------===//
#include <cstdio>
#include <stdexcept>
#include <cstdint>

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/ASTContext.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"

using namespace clang;

class StackFrame {
private:
	/// StackFrame maps Variable Declaration to Value
	/// Which are either integer or addresses (also represented using an Integer value)
	std::map<Decl*, int64_t> mVars;
	std::map<Stmt*, int64_t> mExprs;
	std::map<Stmt*, int64_t> mPtrs;
	/// The current stmt
	Stmt * mPC;
	// Return value
	int64_t rtval;
public:
	StackFrame() : mVars(), mExprs(), mPC() { rtval = 0; }

	void bindDecl(Decl* decl, int64_t val);
	int64_t getDeclVal(Decl * decl);
	void bindStmt(Stmt * stmt, int64_t val);
	int64_t getStmtVal(Stmt * stmt);
	void bindPtr(Stmt * stmt, int64_t addr);
	int64_t getPtr(Stmt * stmt);
	void setPC(Stmt * stmt);
	Stmt * getPC(void);
	int64_t bindRetVal(int64_t val) { return rtval = val; }
	int64_t getRetVal() { return rtval; }
};

// Heap maps address to a value
class Heap {
private:
	std::map<void*, size_t> heap;
public:
	~Heap();
	void * Malloc(int64_t size);
	void Free (void * p);
};

class Environment {
	std::vector<StackFrame> mStack;
	Heap mHeap;

	FunctionDecl * mFree;				/// Declartions to the built-in functions
	FunctionDecl * mMalloc;
	FunctionDecl * mInput;
	FunctionDecl * mOutput;
	FunctionDecl * mEntry;

	const ASTContext& context;
public:
	/// Get the declartions to the built-in functions
	Environment(const ASTContext& ctx) : mStack(), mFree(NULL), mMalloc(NULL),
		mInput(NULL), mOutput(NULL), mEntry(NULL), context(ctx) { }


	/// Initialize the Environment
	void init(TranslationUnitDecl * unit);
	FunctionDecl * getEntry(void);
	void binop(BinaryOperator *bop);
	void unop(UnaryOperator * uop);
	void uett(UnaryExprOrTypeTraitExpr * uettexpr);
	void decl(DeclStmt * declstmt);
	void vardecl(VarDecl * vardecl);
	void fdecl(FunctionDecl * fdecl);
	void declref(DeclRefExpr * declref);
	void bindDecl(Decl* decl, int64_t val);
	void cast(CastExpr * castexpr);
	long call(CallExpr * callexpr);
	void arraysub(ArraySubscriptExpr * asexpr);
	void retstmt(ReturnStmt * retstmt);
	void freturn(CallExpr * callexpr);
	long cond(Expr * condexpr);
	void intlit(IntegerLiteral * intlit);
	void charlit(CharacterLiteral * charlit);
	void paren(ParenExpr * parenexpr);
};
/*
 * An exception class to handle control flow
 * like `return`, `break`, `continue`.
 */
class ControlFlowException {
public:
	enum CFEType {
		RETURN,
		BREAK,
		CONTINUE
	};
private:
	enum CFEType type;
public:
	ControlFlowException(CFEType t) : type(t) { }
	long isReturn() { return type == RETURN; }
	long isBreak() { return type == BREAK; }
	long isContinue() { return type == CONTINUE; }
};
